﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml;

namespace XmlSockets.Internal
{
    /// <summary>
    /// 
    /// </summary>
    internal  class XmlMessageReader : IEnumerator<XmlMessage>
    {
        internal XmlMessageReadBufferdStream stream;

        private static readonly XmlReaderSettings readerSettings = CreateReaderSetting();

        private static readonly XmlParserContext paarserContext = CreateParserContext();


        private static XmlParserContext CreateParserContext()
        {
            return new XmlParserContext(null, null, null, XmlSpace.None, XmlSocketConstant.XmlSocketEncoding); 
        }

        /// <summary>
        /// 
        /// </summary>
        internal int ReceiveTmpBufferSize { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <remarks>
        /// 
        /// </remarks>
        public readonly int MaxAMessageSize;

        private static XmlReaderSettings CreateReaderSetting()
        {
            XmlReaderSettings readerSetting = new XmlReaderSettings();
            
            readerSetting.ConformanceLevel = ConformanceLevel.Fragment;

#if !NET40
            //readerSetting.Async = true;
#endif
            return readerSetting;
            
        }

        private XmlReader CreateReaderInternal()
        {

            return XmlReader.Create(new MemoryStream(this.stream.GetBuffer(), stream.Position, stream.Length - stream.Position), readerSettings, paarserContext);
        }

        public void AfterRead()
        {
            stream.AfterRead();

        }

        public void Clear()
        {
            stream.Clear();
        }




        internal XmlMessageReader(int receiveBufferSize,int receiveTmpBufferSize,int maxAMessageSize = 65535)
        {
            if (receiveBufferSize < 0 || receiveTmpBufferSize < 0) throw new ArgumentOutOfRangeException();
            stream = new XmlMessageReadBufferdStream(receiveBufferSize);
            ReceiveTmpBufferSize = receiveTmpBufferSize;
            MaxAMessageSize = maxAMessageSize;
            this._current = new XmlMessage();
            
        }



        /// <summary>
        /// 
        /// </summary>
        public void ReadContent(IXmlSocketReceiveContent content)
        {
             this.stream.ReadContent(content);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public MessageReadState NextMessage()
        {   
            MessageReadState rslt= this.stream.NextMessage();

            if (rslt == MessageReadState.NotCmpleted && MaxAMessageSize >= 0)
            {
                if (MaxAMessageSize < this.stream.Length - this.stream.Position) throw new InvalidOperationException("Received message size is more then max size");
            }

            return rslt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public XmlReader CreateReader()
        {
            return ReadAction(() => CreateReaderInternal());
        }

        



        private T ReadAction<T>(Func<T> func)
        {
            if (this.stream.MessageAvailable)
            {

                this.stream.MessageAvailable = false;
                return func();


            }
            else
            {

                return default(T);
            }

        }

        public void SetAvaileRead()
        {
            this._availeRead = true;
        }

        public void ResetAvaileRead()
        {
            this._availeRead = false;
            this._current.Reader = null;
            stream.ResturctBuffer();
            DisporseCurrentReader();
            
        }

        private void CheckAvaileRead()
        {
            if (!_availeRead) throw new InvalidOperationException("Can not read now");
        }

        private bool _availeRead;
        private XmlMessage _current;

        public XmlMessage Current
        {
            get 
            {
                CheckAvaileRead();
                return _current; 
            }
        }

        object IEnumerator.Current
        {
            get 
            {
                CheckAvaileRead();
                return _current; 
            }
        }

        

        public bool MoveNext()
        {

            DisporseCurrentReader();
            CheckAvaileRead();


            switch (this.NextMessage())
            {
                case MessageReadState.Completed:
                    return OnCompleted();

                case MessageReadState.NotCmpleted:
                    return OnNotCompleted();

                case MessageReadState.NotXmlMessage:
                    return OnNotXmlMessage();

                default:
                    return false;

            }

        }

        public void Reset()
        {
            throw new NotSupportedException();
        }

        public void Dispose()
        {
        }

        private bool OnNotXmlMessage()
        {
            this._current.Reader = null;
            AfterRead();
            return MoveNext();
        }

        private bool OnNotCompleted()
        {
            this._current.Reader = null;
            return false;
        }

        private void DisporseCurrentReader()
        {
            if (this._current.Reader != null)
            {
#if NET40
                this._current.Reader.Close();
#else
                this._current.Reader.Dispose();
#endif
                this._current.Reader = null;
            }
        }

        private bool OnCompleted()
        {
            this._current.Reader = CreateReader();
            AfterRead();
            return true;
        }
    }
}
